#ifndef _SERVER_
#define _SERVER_
#include "log.hpp"
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <unordered_map>

#define SIZE 1024

class UdpServer
{
public:
    UdpServer(uint16_t port,std::string ip = ""):_ip(ip),_port(port)
    {}

    bool initServer()
    {
        //1.创建套接字
        _socket = socket(AF_INET,SOCK_DGRAM,0);
        //创建失败
        if(_socket < 0)
        {
            logMessage(FATAL,"%d-%s",errno,strerror(errno));
            exit(-1);
        }
        //2.使用bind进行绑定
        //int bind(int socket, const struct sockaddr *address,socklen_t address_len);
        //所以我们需要先创建一个sockaddr,并初始化
        struct sockaddr_in local;
        bzero(&local,sizeof(local));
        local.sin_family = AF_INET;  //家族地址
        local.sin_port = htons(_port); //必须要转成网络形式
        //sin_addr存储IP地址，使用in_addr这个数据结构
        //s_addr按照网络字节顺序存储IP地址
        //inet_addr() 函数的作用是将点分十进制的IPv4地址转换成网络字节序列的长整型
        //INADDR_ANY 可以在服务器工作中，从任意ip获取数据
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        //开始bind
        if(bind(_socket,(struct sockaddr*)&local,sizeof(local)) < 0)
        {
            //如果绑定失败
            logMessage(FATAL,"%d-%s",errno,strerror(errno));
            exit(2);
        }
        logMessage(NORMAL,"bind done...%s",strerror(errno));
        return true;
    }

    void start()
    {
        //服务器要一直不退出
        char buffer[SIZE];
        while(1)
        {
            //用一个输出型参数接受传来的sockaddr
            struct sockaddr_in rev; 
            bzero(&rev,sizeof rev);
            //获取长度
            socklen_t len = sizeof rev;
            //3.接收消息
            ssize_t s = recvfrom(_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&rev,&len);
            //实现
            std::string echo_cmd;
            char result[256];
            char key[64];
            if(s > 0)
            {
                //3.1 普通输出
                // buffer[s] = 0;  //目前数据当作字符串
                // uint16_t f_port = ntohs(rev.sin_port);  //网络地址转成主机地址
                // std::string f_ip = inet_ntoa(rev.sin_addr);
                // printf("[%s]:%d say->%s\n",f_ip.c_str(),f_port,buffer);

                //3.2用popen执行shell命令
                //防止输入rm
                // buffer[s] = 0;
                // if(strcasestr(buffer,"rm") != nullptr && strcasestr(buffer,"rmdir") != nullptr)
                // {
                //     std::string back = "dont do that..";
                //     logMessage(ERROR,"dont do that..");
                //     sendto(_socket,back.c_str(),back.size(),0,(struct sockaddr *)&rev,len);
                // }
                // FILE* fp = popen(buffer,"r");
                // if(fp == nullptr)
                // {
                //     logMessage(ERROR,"rev failed .. %d-%s",errno,strerror(errno));
                //     continue;
                // }
                // while(fgets(result,sizeof(result),fp) != nullptr)
                // {
                //     echo_cmd += result;
                // }
                // fclose(fp);

                //3.3 聊天室的实现
                buffer[s] = 0;
                uint16_t userport = ntohs(rev.sin_port);
                std::string userip = inet_ntoa(rev.sin_addr);
                //规范化一下key
                snprintf(key,sizeof key,"%s-%d",userip.c_str(),userport);
                //查找他们的信息，没有就存进去
                auto it = _users.find(key);
                if(it == _users.end())
                {
                    _users[key] = rev;
                } 
            }
            //3.1 还需要返还回去
            //sendto(_socket,buffer,sizeof(buffer),0,(struct sockaddr *)&rev,len);
            //3.2 获取命令并发回去
            //sendto(_socket,echo_cmd.c_str(),echo_cmd.size(),0,(struct sockaddr *)&rev,len);
            //3.3 多人聊天室
            //先遍历每一个用户
            for(auto &it:_users)
            {
                std::string message = key;
                message += "-> ";
                message += buffer; //ip+port-> 信息
                sendto(_socket,buffer,sizeof(buffer),0,(struct sockaddr*)(&it.second),sizeof(it.second));
            }
        }
    }

    ~UdpServer(){
        if(_socket > 0)
            close(_socket);
    }
private:
    std::string _ip; //ip地址
    uint16_t _port; //端口
    int _socket; //套接字
    std::unordered_map<std::string,struct sockaddr_in> _users;
};

#endif